package sk.ygor.raynor.rebind.serializer

import com.google.gwt.core.ext.typeinfo.{JType, JClassType}
import com.google.gwt.core.ext.{UnableToCompleteException, GeneratorContext, TreeLogger, Generator}
import sk.ygor.raynor.client.json.{JSONSerializationException, JSONSerializer}
import sk.ygor.raynor.rebind.serializable.JSONSerializableGenerator
import sk.ygor.xtemplate.XTemplate
import com.google.gwt.user.rebind.{SourceWriter, ClassSourceFileComposerFactory}
import com.google.gwt.json.client.JSONValue
class JSONSerializerGenerator extends Generator {

  var serializerType: JClassType = null
  var serializableType: JType = null
  var logger: TreeLogger = null
  var context: GeneratorContext = null
  var generatedClassPackage: String = null
  var generatedClassName: String = null

  def generate(logger: TreeLogger, context: GeneratorContext, inputClassName: String): String = {
    this.logger = logger;
    this.context = context;
    try {
      serializerType = context.getTypeOracle().getType(inputClassName);
      serializableType = isSpecifiedByJSONSerializerInterface(serializerType);
      this.generatedClassPackage = serializerType.getPackage().getName();
      this.generatedClassName = serializerType.getSimpleSourceName() + "Impl";
      generateClass;
      return generatedClassPackage + "." + generatedClassName;
    } catch {
      case e => {
        logger.log(TreeLogger.ERROR, "Generator ERROR!!!", e);
        throw new UnableToCompleteException();
      }
    }
  }

  def generateClass {
    val printWriter = context.tryCreate(logger, generatedClassPackage, generatedClassName);
    // print writer if null, source code has ALREADY been generated, return
    if (printWriter == null) {
      return;
    }
    val composer = new ClassSourceFileComposerFactory(generatedClassPackage, generatedClassName);

    composer.addImplementedInterface(serializerType.getParameterizedQualifiedSourceName());
    composer.addImplementedInterface(classOf[JSONSerializer.Configuration].getName.replaceAll("\\$", "\\."))
    
    composer.addImport(classOf[JSONSerializer[_]].getName());
    composer.addImport(classOf[JSONValue].getName());

    val sourceWriter = composer.createSourceWriter(context, printWriter);
    generateBody(sourceWriter);

    sourceWriter.outdent();
    sourceWriter.println("}");

    context.commit(logger, printWriter);
  }

  def generateBody(sw: SourceWriter) = {
    val xtpl = new XTemplate("sk/ygor/raynor/rebind/serializer/JSONSerializer.xtpl");
    xtpl.assign("delegateSerializerClassName", new JSONSerializableGenerator().generate(logger, context, serializableType));
    xtpl.assign("serializableClassName", serializableType.getParameterizedQualifiedSourceName());
    xtpl.parse();
    sw.println(xtpl.getText());
  }

  def isSpecifiedByJSONSerializerInterface(inputClassType: JType): JType = {
    val jClassType = inputClassType.isInterface();
    if (jClassType == null) {
      return null;
    }
    for (anInterface: JClassType <- jClassType.getImplementedInterfaces()) {
      if (anInterface.getQualifiedSourceName() == (classOf[JSONSerializer[_]].getName())) {
        val jParameterizedType = anInterface.isParameterized();
        if (jParameterizedType == null) {
          throw new JSONSerializationException("JSONSerializer interface must be parametrized.");
        }
        return jParameterizedType.getTypeArgs()(0);
      }
    }
    throw new JSONSerializationException(inputClassType.toString() + " doesn't implement JSONSerializer interface.");
  }


}